﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharedContent;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;

using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics.PackedVector;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace MiaProject
{
    class LevelManager
    {
        public Vector3 camPos;
        public Level level;
        public List<Platform> platformList;
        public List<Tower> towerList;
        public List<Ship> shipList;
        public List<Portal> portalList;
        public List<PowerUp> powerUpList;
        public List<Build> buildList;
        
        public List<Model> modelList;
        
        
        private List<String> modelListFile;
        
        
        public const int LEVEL_ALIVE = 1;
        public const int LEVEL_FINISH = 2;
        public const int LEVEL_LOADING = 3;
        
        public int nrEnemysKill;
        public int nrPowerUpsCatch;
        public int score;

        public int totalEnemiesKill;
        public int totalPowerUpsCatch;
        public int totalEnemies;
        public int totalPowerUps;
    
        public int levelStatus;
        public String nextLevel;

        
        private ContentManager content;
            
        private static LevelManager instance;
        

        private LevelManager()
        {
            towerList = new List<Tower>();
            shipList = new List<Ship>();
            platformList = new List<Platform>();
            portalList = new List<Portal>();
            powerUpList = new List<PowerUp>();
            buildList = new List<Build>();

            modelList = new List<Model>();
            modelListFile = new List<String>();


            camPos = new Vector3(0.0f, 1000.0f, 600.0f);


            levelStatus = LEVEL_ALIVE;
            nextLevel = "Loguetown";
            
            
            
            nrEnemysKill = 0;
            nrPowerUpsCatch = 0;
            score = 0;

            totalEnemiesKill = 0; 
            totalPowerUpsCatch = 0;
            totalEnemies = 0;
            totalPowerUps = 0;
    

            Sound s = Sound.getInstance();



        }
        public static LevelManager getInstance()
        {
            if (instance == null)
                instance = new LevelManager();
            return instance;
        }

        
        public void load(ContentManager content,String xmlName)
        {
            this.content = content;
            this.loadLevel(xmlName);

            
        }//end of method
        private void loadLevel(String xmlName)
        {
            System.Console.WriteLine("~~~~~~~~~LEVEL~~~~~~~~~~~~~~~~");

            level = content.Load<Level>("level/"+xmlName);
            System.Console.WriteLine(level.name);

            
            this.loadPlatform();
            this.loadTower();
            this.loadShip();
            this.loadPortal();
            this.loadPowerUp();
            //this.loadBuild();

            System.Console.WriteLine("~~~ROOMS~~~~");
            foreach (Room r in level.rooms)
            {
                System.Console.WriteLine(r.posMin.ToString());
                System.Console.WriteLine(r.posMax.ToString());
            }
            
            levelStatus = LEVEL_ALIVE;

            Sound s = Sound.getInstance();
            
            s.setGameVolume();
        }
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        public void update(GameTime gameTime)
        {
            if(levelStatus != LEVEL_ALIVE)
                return;
            foreach (Tower t in towerList)
                t.update(gameTime);

            foreach (Ship s in shipList)
                s.update(gameTime);
            
            foreach (Portal p in portalList)
                p.update(gameTime);
            
            foreach (PowerUp p in powerUpList)
                p.update(gameTime);
            
            this.verifyCollisionPlayer();
            this.verifyCollisionEnemy();
            this.verifyCollisionPowerUp();
            this.verifyCollisionPortal();

            
        }//end of mehtod
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        
        public void loadNextLevel()
        {
            //fazer unload das listas
            towerList = new List<Tower>();
            shipList = new List<Ship>();
            platformList = new List<Platform>();
            portalList = new List<Portal>();
            powerUpList = new List<PowerUp>();
            buildList = new List<Build>();
            
            Player player = Player.getInstance();
            player.pos.X = 0;
            player.pos.Z = 0;
            player.rotation = 0.0f;
            player.life = 1.0f;
            player.lastRoom = 0;
            player.typeTiro = 1;
            
            nrEnemysKill = 0;
            nrPowerUpsCatch = 0;
            
            this.loadLevel(this.nextLevel);
            
        }
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        
        /*private void loadBuild()
        {
            foreach (BuildContent build in level.builds)
            {
                System.Console.WriteLine(build.name);

                Build b = new Build();
                
                b.name   = build.name;
                b.pos = build.position;
                b.rotation = build.rotation;
                b.model = this.getModel(build.file);
                
                buildList.Add(b);
            }
        }//end of mehtod*/
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        
        private void loadPlatform()
        {
            foreach (PlatformContent plat in level.platforms)
            {
                System.Console.WriteLine(plat.name);

                Platform platform = new Platform();
                platform.name   = plat.name;
                platform.pos = plat.position;
                platform.rotation = plat.rotation;
                platform.model  = this.getModel(plat.file);
                
                platformList.Add(platform);
            }
        }//end of mehtod
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        private void loadTower()
        {
            foreach (TowerContent t in level.towers)
            {
                System.Console.WriteLine(t.name);

                Tower tower = new Tower();

                tower.name   = t.name;
                tower.pos = t.position;
                tower.scale = t.scale;
                tower.pos = t.position;
                tower.life = t.life;
                tower.rotationStart = t.rotation;
                tower.scaleCollision = t.scaleCollision;
                tower.tiroFrequence = t.tiroFrequence;
                tower.distTargetAtack = t.distTargetAtack;
                tower.distTiroPosStart = t.distTiroPosStart;

                tower.model  = this.getModel(t.file);
                tower.tiro  = this.getModel(t.fileTiro);                
                tower.soundTiro  = t.fileSoundTiro;
                tower.soundTiroCollision  = t.fileSoundCollision;
                tower.soundDead = t.fileSoundDead;

                if (t.name == "Tower02")
                {
                    tower.scaleTiro = 3.0f;
                    tower.distTiroPosStart = new Vector3(300,10,400);                    
                }
                

                towerList.Add(tower);
            }
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        private void loadShip()
        {
            foreach (ShipContent t in level.ships)
            {
                System.Console.WriteLine(t.name);

                Ship ship = new Ship();

                if (t.name == "Tank01" || t.name == "Tank02" || t.name == "Tank03")
                {
                    ship.scaleTiro = 2.0f;
                    ship.distTiroPosStart = new Vector3(100, 60, 100);
                }

                ship.name   = t.name;
                ship.pos = t.position;
                ship.scale = t.scale;
                ship.pos = t.position;
                ship.life = t.life;
                ship.speed = t.speed;
                ship.speedOriginal = t.speed;
                ship.rotationStart = t.rotation;
                ship.scaleCollision = t.scaleCollision;
                ship.tiroFrequence = t.tiroFrequence;
                ship.distTargetAtack = t.distTargetAtack;
                
                ship.model  = this.getModel(t.file);
                ship.tiro  = this.getModel(t.fileTiro);                
                ship.soundTiro  = t.fileSoundTiro;
                ship.soundTiroCollision  = t.fileSoundCollision;
                ship.soundDead = t.fileSoundDead;

                ship.points = t.points;
                
                System.Console.WriteLine("~~~POINTS~~~~");
                foreach(Vector3 v in t.points)
                    System.Console.WriteLine(v.X + "," + v.Y + "," + v.Z);
                
                shipList.Add(ship);
            }
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        private void loadPortal()
        {
            foreach (PortalContent t in level.portals)
            {
                System.Console.WriteLine(t.destiny);

                Portal portal = new Portal();
                
                portal.destiny = t.destiny;
                portal.pos = t.position;
                portal.scale = t.scale;
                portal.rotationStart = t.rotation;
                portal.scaleCollision = t.scaleCollision;
                
                portal.model  = this.getModel(t.file);
                portal.soundTiroCollision  = t.fileSoundCollision;
                
                portalList.Add(portal);
            }
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        private void loadPowerUp()
        {
            foreach (PowerUpContent t in level.powerups)
            {
                System.Console.WriteLine(t.name);

                PowerUp pu = new PowerUp();
                
                pu.name = t.name;
                pu.life = t.life;
                pu.power = t.power;
                pu.pos = t.position;
                pu.scale = t.scale;
                pu.rotationStart = t.rotation;
                pu.scaleCollision = t.scaleCollision;
                
                pu.model  = this.getModel(t.file);
                pu.soundTiroCollision  = t.fileSoundCollision;
                
                powerUpList.Add(pu);
            }
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        
        
        
        /**
            Verifica se o modelo ja nao foi carregado, caso sim utiliza
            senao carrega e add a lista
        */
        private Model getModel(String file)
        {
            //verifica se ja nao foi carregado
            int i = 0;
            foreach(String f in modelListFile)
            {
                if(f.Equals(file))
                    return (Model)modelList[i];
                i++;
            }
            
            Model m = content.Load<Model>(file);
            
            modelList.Add(m);
            modelListFile.Add(file);
            
            return m;
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        
        
        
        /*
            Desenha os itens do level
            - Plataforms
            - Towers
            - Ships
        */
        public void draw(GraphicsDevice device,Matrix view, Matrix projection,BoundingFrustum frustum)
        {
            if(levelStatus != LEVEL_ALIVE)
                return;
            device.BlendState = BlendState.Opaque;
            device.DepthStencilState = DepthStencilState.Default;
            device.SamplerStates[0] = SamplerState.LinearWrap;

           
            foreach(Platform platform in platformList)
                platform.draw(view, projection,frustum);
            
            foreach(Tower tower in towerList)
            {
                tower.draw(view, projection,frustum);
            }
            foreach(Ship ship in shipList)
                ship.draw(view, projection,frustum);
            
            foreach (Portal p in portalList)
                p.draw(view, projection,frustum);
            
            foreach (PowerUp p in powerUpList)
                p.draw(view, projection,frustum);

            foreach (Build b in buildList)
                b.draw(view, projection, frustum);


        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /**
        Colisao de tiros com inimigos (ship e tower)
        */
        private void verifyCollisionEnemy()
        {
            ParticleManager pm = ParticleManager.getInstance();
            Player p = Player.getInstance();
            Sound s = Sound.getInstance();
            BoundingSphere c1, c2;
            foreach (Tiro t in p.tiros)
            {
                if (t == null || !t.status)
                    continue;
                c1 = new BoundingSphere(t.pos, p.tiro.Meshes[0].BoundingSphere.Radius);

                //~~~~ verify with shipList~~~~~~~~~~~~~~ 
                foreach (Ship e in shipList)
                {
                    if (e == null || !e.status)
                        continue;
                    c2 = new BoundingSphere(e.pos, e.model.Meshes[0].BoundingSphere.Radius * e.scaleCollision);// * 2
                    if (c1.Intersects(c2))
                    {
                        e.life -= p.powerAtack;
                        if (e.life <= 0)
                        {
                            e.status = false;
                            nrEnemysKill++;


                            
                            s.soundBank.PlayCue(e.soundDead);
                        }
                        else
                            s.soundBank.PlayCue(e.soundTiroCollision);
                        
                        t.status = false;                        
                        pm.addExplosion(e.pos);
                        break;
                    }
                }//end for

                //verify with towers
                foreach (Tower e in towerList)
                {
                    if (e == null || !e.status)
                        continue;
                    c2 = new BoundingSphere(e.pos, e.model.Meshes[0].BoundingSphere.Radius * e.scaleCollision);// * 2
                    if (c1.Intersects(c2))
                    {
                        e.life -= p.powerAtack;

                        if (e.life <= 0)
                        {
                            e.status = false;
                            nrEnemysKill++;
                            s.soundBank.PlayCue(e.soundDead);
                        }
                        else
                            s.soundBank.PlayCue(e.soundTiroCollision);
                        

                        t.status = false;
                        pm.addExplosion(e.pos);
                        break;
                    }
                }//end for
            }//end for tiros player
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /**
        Colisao de tiros com player
        */
        private void verifyCollisionPlayer()
        {
            ParticleManager pm = ParticleManager.getInstance();
            Player p = Player.getInstance();
            Sound s = Sound.getInstance();

            BoundingSphere c1, c2;
            c1 = new BoundingSphere(p.pos, p.model.Meshes[0].BoundingSphere.Radius * 0.05f);// * 2

            //~~~~ verify with shipList~~~~~~~~~~~~~~ 
            foreach (Ship e in shipList)
            {
                if (e == null || !e.status)
                    continue;

                foreach (Tiro t in e.tiros)
                {
                    if (t == null || !t.status)
                        continue;

                    c2 = new BoundingSphere(t.pos, e.tiro.Meshes[0].BoundingSphere.Radius * e.scaleCollision);// * 2
                    if (c1.Intersects(c2))
                    {
                        t.status = false;
                        p.setAttack(0.1f);
                        //gameScore += e.score;
                        //p.soundTiroCollision.Play();
                        if (p.life > 0)
                            s.soundBank.PlayCue("player_collide");
                        
                        break;
                    }
                }//end for
            }//end for
            //~~~~ verify with shipList~~~~~~~~~~~~~~ 

            foreach (Tower e in towerList)
            {
                if (e == null || !e.status)
                    continue;

                foreach (Tiro t in e.tiros)
                {
                    if (t == null || !t.status)
                        continue;

                    c2 = new BoundingSphere(t.pos, e.tiro.Meshes[0].BoundingSphere.Radius * e.scaleCollision);// * 2
                    if (c1.Intersects(c2))
                    {
                        /*if (p.life <= 0)
                            p.status = false;

                        p.life -= e.powerAtack;
                        */
                        p.setAttack(0.1f);
                        t.status = false;
                        //gameScore += e.score;
                        //e.soundColision.Play();
                        //pm.addExplosion(p.pos);
                        if (p.life > 0)
                            s.soundBank.PlayCue("player_collide");
                        
                        break;
                    }
                }//end for
            }//end for
            if (p.life <= 0)
            {
                s.soundBank.PlayCue("explosion");
                pm.addExplosion(p.pos);
            }
                        
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        /**
        Colisao de player com portal
        */
        private void verifyCollisionPortal()
        {
            ParticleManager pm = ParticleManager.getInstance();
            Player p = Player.getInstance();
            Sound s = Sound.getInstance();

            BoundingSphere c1, c2;
            c1 = new BoundingSphere(p.pos, p.model.Meshes[0].BoundingSphere.Radius * 0.05f);// * 2

            //~~~~ verify with shipList~~~~~~~~~~~~~~ 
            foreach (Portal e in portalList)
            {
                if (e == null || !e.status)
                    continue;

                c2 = new BoundingSphere(e.pos, e.model.Meshes[0].BoundingSphere.Radius * e.scaleCollision);// * 2
                if (c1.Intersects(c2))
                {
                    e.status = false;
                    //this.loadNextLevel(e);
                    this.nextLevel = e.destiny;
                    this.levelStatus = LEVEL_FINISH;
                    this.score += nrEnemysKill*3 + nrPowerUpsCatch*2;
                    this.totalEnemiesKill   += nrEnemysKill;
                    this.totalPowerUpsCatch += nrPowerUpsCatch;
                    this.totalEnemies       += towerList.Count + shipList.Count;
                    this.totalPowerUps      += powerUpList.Count;

                    s.soundBank.PlayCue(e.soundTiroCollision);
                    return;
                }
            }//end for
            //~~~~ verify with shipList~~~~~~~~~~~~~~ 
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        /**
        Colisao de player com powerUp
        */
        private void verifyCollisionPowerUp()
        {
            ParticleManager pm = ParticleManager.getInstance();
            Player p = Player.getInstance();
            Sound s = Sound.getInstance();

            BoundingSphere c1, c2;
            c1 = new BoundingSphere(p.pos, p.model.Meshes[0].BoundingSphere.Radius * 0.05f);// * 2

            //~~~~ verify with shipList~~~~~~~~~~~~~~ 
            foreach (PowerUp e in powerUpList)
            {
                if (e == null || !e.status)
                    continue;

                c2 = new BoundingSphere(e.pos, e.model.Meshes[0].BoundingSphere.Radius * e.scaleCollision);// * 2
                if (c1.Intersects(c2))
                {
                    e.status = false;
                    if (e.life > 0)
                        p.addLife(e.life);
                    if (e.power > 0)
                        p.typeTiro = e.power;

                    //gameScore += e.score;
                    //p.soundTiroCollision.Play();
                    nrPowerUpsCatch++;
                    pm.addExplosion(e.pos);
                    s.soundBank.PlayCue(e.soundTiroCollision);
                    break;
                }
            }//end for
            //~~~~ verify with shipList~~~~~~~~~~~~~~ 
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        
        /**
        Verifica se está em alguma plataforma do mapa.
        @param Vector3 pos = posicao do objeto que deseja verificar
        @param int lastRoom = indice da ultima plataforma
        @return int indice para a room, -1 caso nao encontre nenhuma
        */
        public int inRoom(Vector3 pos,int lastRoom = 0)
        {
            //verifica se nao continua na ultima room (nesse caso nao precisa verificar em todas sempre)
            if (lastRoom != -1)
            {
                Room r = level.rooms[lastRoom];
                if (pos.X >= r.posMin.X && pos.X <= r.posMax.X && pos.Z >= r.posMin.Z && pos.Z <= r.posMax.Z)
                    return lastRoom;
            }
            
            //nao está na ultima detectada, verificar no restante
            int i = 0;
            foreach(Room r1 in level.rooms)
            {
                if(i != lastRoom && pos.X >= r1.posMin.X && pos.X <= r1.posMax.X && pos.Z >= r1.posMin.Z && pos.Z <= r1.posMax.Z)
                    return i;//está em alguma plataform
                i++;
            }
            return -1;//verificou em todas e nao está em nenhuma.
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 


    }
}
